import typeOfUtils from './type-of-utils'

const htmlElementUtils = {
  /**
   * 获取元素样式
   * @param el {HTMLElement}
   * @param style {String}
   * @return {string}
   */
  getStyle(el: HTMLElement, style: string): string {
    if (typeOfUtils.isNotHTMLDocument(el)) {
      throw new Error('el元素不是dom')
    }
    return window.getComputedStyle(el, null).getPropertyValue(style)
  },
  /**
   *
   * @param el {HTMLElement}
   * @param child {HTMLElement}
   */
  addChild(el: HTMLElement, child: HTMLElement): void {
    if (typeOfUtils.isNotHTMLDocument(el)) {
      throw new Error('父元素不是dom')
    }
    if (typeOfUtils.isNotHTMLDocument(child)) {
      throw new Error('子元素不是dom')
    }
    el.appendChild(child)
  },
  /**
   *
   * @param el {HTMLElement}
   * @param child {HTMLElement}
   */
  removeChild(el: HTMLElement, child: HTMLElement): void {
    if (typeOfUtils.isNotHTMLDocument(el)) {
      throw new Error('父元素不是dom')
    }
    if (typeOfUtils.isNotHTMLDocument(child)) {
      throw new Error('子元素不是dom')
    }
    if (this.hasChildren(el, child)) {
      el.removeChild(child)
    }
  },
  /**
   * 判断是不是有这个子元素
   * @param ele {HTMLElement}
   * @param child {HTMLElement}
   * @return {Boolean|*}
   */
  hasChildren(ele: HTMLElement, child: HTMLElement): boolean {
    // 判断是不是元素
    if (ele !== child) {
      return ele.contains(child)
    } else {
      throw new Error('不是父子元素')
    }
  },
  /**
   * 获取元素是否在可视区域
   * @param el {HTMLElement}
   * @return {boolean|boolean}
   */
  isElementInViewport(el: HTMLElement): boolean {
    const rect = el.getBoundingClientRect()
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <=
      (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <=
      (window.innerWidth || document.documentElement.clientWidth)
    )
  },
  /**
   * 判断元素是否在指定元素的可视区内，有一点点在也算在
   * @param el {HTMLElement} 元素
   * @param container {HTMLElement} 指定元素
   * @returns {boolean}
   */
  isInContainer(el: HTMLElement, container: HTMLElement): boolean {
    if (!el || !container) return false;
    const elRect = el.getBoundingClientRect();
    let containerRect;
    if ([window, document, document.documentElement, null, undefined].includes(container)) {
      containerRect = {
        top: 0,
        right: window.innerWidth,
        bottom: window.innerHeight,
        left: 0
      };
    } else {
      containerRect = container.getBoundingClientRect();
    }
    return elRect.top < containerRect.bottom &&
      elRect.bottom > containerRect.top &&
      elRect.right > containerRect.left &&
      elRect.left < containerRect.right;
  },
  /**
   * 判断元素是否在指定元素的可视区内，全部
   * @param el
   * @param container
   */
  isAllInContainer(el: HTMLElement, container: HTMLElement): boolean {
    if (!el || !container) return false;
    const isIn = el.offsetLeft >= container.offsetLeft
      && (el.offsetLeft + el.offsetWidth) <= (container.offsetLeft + container.offsetWidth);
    if (!isIn) {
      return false;
    }
    return el.offsetTop >= container.offsetTop
      && (el.offsetTop + el.offsetHeight) <= (container.offsetTop + container.offsetHeight)
  },
  /**
   * 判断元素是否在指定元素的可视区内，全部-水平方向
   * @param el
   * @param container
   */
  isAllInContainerH(el: HTMLElement, container: HTMLElement): boolean {
    if (!el || !container) return false;
    return el.offsetLeft >= container.offsetLeft
      && (el.offsetLeft + el.offsetWidth) <= (container.offsetLeft + container.offsetWidth);
  },
  /**
   * 判断元素是否在指定元素的可视区内，全部-垂直方向
   * @param el
   * @param container
   */
  isAllInContainerV(el: HTMLElement, container: HTMLElement): boolean {
    if (!el || !container) return false;
    return el.offsetTop >= container.offsetTop
      && (el.offsetTop + el.offsetHeight) <= (container.offsetTop + container.offsetHeight)
  },
  /**
   * 元素到左边的距离, 如果parent为空就是到window 的距离
   * @param ele {HTMLElement}
   * @param parent {HTMLElement?}
   */
  elementToLeft(ele: HTMLElement, parent?: HTMLElement): number {
    if (parent) { // 有父元素
      if (this.hasChildren(parent, ele)) { // 判断是不是父子关系
        const leftELe = this.elementToLeft(ele)
        const leftPar = this.elementToLeft(parent)
        return leftELe - leftPar
      }
    } else { // 没有父元素,计算到window的距离
      if (typeOfUtils.isNotEmpty(ele.offsetLeft)) {
        let tmp = ele.offsetLeft
        let val = ele.offsetParent as HTMLElement
        while (val != null) {
          tmp += val.offsetLeft
          val = val.offsetParent as HTMLElement
        }
        return tmp
      }
    }
    return 0
  },
  /**
   * 元素到右边的距离, 如果parent为空就是到window 的距离
   * @param ele {HTMLElement}
   * @param parent {HTMLElement?}
   */
  elementToRight(ele: HTMLElement, parent?: HTMLElement): number {
    const left = this.elementToLeft(ele, parent)
    if (parent) { // 有父元素
      if (this.hasChildren(parent, ele)) { // 判断是不是父子关系
        const pwidth = parent.offsetWidth
        const cwidth = ele.offsetWidth
        return pwidth - left - cwidth
      }
    } else { // 没有父元素,计算到window的距离
      const cwidth = ele.offsetWidth
      return document.body.offsetWidth - cwidth - left
    }
    return 0
  },
  /**
   * 元素到顶部的距离, 如果parent为空就是到window 的距离
   * @param ele {HTMLElement}
   * @param parent {HTMLElement?}
   */
  elementToTop(ele: HTMLElement, parent?: HTMLElement): number {
    if (parent) { // 有父元素
      if (this.hasChildren(parent, ele)) { // 判断是不是父子关系
        const topELe = this.elementToTop(ele)
        const topPar = this.elementToTop(parent)
        return topELe - topPar
      }
    } else { // 没有父元素,计算到window的距离
      if (typeOfUtils.isNotEmpty(ele.offsetTop)) {
        let tmp = ele.offsetTop
        let val = ele.offsetParent as HTMLElement
        while (val != null) {
          tmp += val.offsetTop
          val = val.offsetParent as HTMLElement
        }
        return tmp
      }
    }
    return 0
  },
  /**
   * 元素到底部的距离, 如果parent为空就是到window 的距离
   * @param ele {HTMLElement}
   * @param parent {HTMLElement?}
   */
  elementToBottom(ele: HTMLElement, parent?: HTMLElement): number {
    const top = this.elementToTop(ele, parent)
    if (parent) { // 有父元素
      if (this.hasChildren(parent, ele)) { // 判断是不是父子关系
        const pheight = parent.offsetHeight
        const cheight = ele.offsetHeight
        return pheight - top - cheight
      }
    } else { // 没有父元素,计算到window的距离
      const cheight = ele.offsetHeight
      return document.body.offsetHeight - cheight - top
    }
    return 0
  },
  /**
   * 元素在X轴滚动
   * @param element {HTMLElement}
   * @param children {HTMLElement}
   * @param position {string|number|Function} 子元素显示在父级的那个位置
   */
  elementScrollX(element: HTMLElement, children: HTMLElement, position: string | number | Function = 'center'): void {
    if (position === 'center') {
      let number = parseFloat(this.getStyle(element, 'height'));
      number = number / 2
      let top = this.elementToTop(children, element)
      top = top - number
      element.scrollTop = top
    }
  },
  /**
   * 查找可以在X轴滚动的父级元素
   * @param children {HTMLElement}
   * @returns {null|*|HTMLElement|HTMLElement}
   */
  findScrollXParentElement(children: HTMLElement): null | HTMLElement {
    let parentElement = children.parentElement
    while (parentElement != null) {
      if (this.eleCanScrollX(parentElement)) {
        return parentElement
      }
      parentElement = parentElement.parentElement
    }
    return null
  },
  /**
   * 判断元素是否可以X轴滚动
   * @param ele {HTMLElement}
   * @returns {boolean}
   */
  eleCanScrollX(ele: HTMLElement): boolean {
    if (typeOfUtils.isNotHTMLDocument(ele)) {
      console.error('不是html元素')
      return false
    }
    if (ele.scrollTop > 0) {
      return true
    } else {
      ele.scrollTop++
      // 元素不能滚动的话，scrollTop 设置不会生效，还会置为 0
      const top = ele.scrollTop
      // 重置滚动位置
      top && (ele.scrollTop = 0)
      return top > 0
    }
  },
  /**
   * 判断text-overflow: ellipsis(是否溢出)
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
   * @param ele {HTMLElement}
   * @param dir
   * @param easy
   * @returns {boolean}
   */
  eleHasEllipsis(ele: HTMLElement,dir:'h'|'w'|'a' = "a",easy=false): boolean {
    if (typeOfUtils.isNotHTMLDocument(ele)) {
      console.error('不是html元素')
      return false
    }
    if (ele.clientWidth < ele.scrollWidth && ['a','w'].includes(dir)) {
      return true
    }
    if (ele.clientHeight < ele.scrollHeight && ['a','h'].includes(dir)) {
      return true
    }
    if(easy) {
      return false
    }
    const clone = ele.cloneNode(true) as HTMLElement
    clone.style.visibility = 'hidden'
    clone.style.display = 'inline-block'
    clone.style.width = 'auto'
    clone.style.position = 'fixed'
    if (ele.parentElement) {
      ele.parentElement.appendChild(clone)
      let result = false
      if (ele.clientWidth < clone.clientWidth && ['a','w'].includes(dir)) {
        result = true
      }
      if (ele.clientHeight < clone.scrollHeight && ['a','h'].includes(dir)) {
        result = true
      }
      ele.parentElement.removeChild(clone)
      return result
    } else {
      return false
    }
  },
  hasClass(ele: HTMLElement, cls: string) {
    return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
  },
  addClass(ele: HTMLElement, cls: string) {
    if (!this.hasClass(ele, cls)) ele.className += ' ' + cls
  },
  removeClass(ele: HTMLElement, cls: string) {
    if (this.hasClass(ele, cls)) {
      const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
      ele.className = ele.className.replace(reg, ' ')
    }
  }
}

export default htmlElementUtils
